package net.apexcns.core.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.commons.io.IOUtils;

import net.apexcns.core.util.xstream.CdataXppDriver;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.QNameMap;
import com.thoughtworks.xstream.io.xml.StaxDriver;

@SuppressWarnings("unchecked")
public class XmlBindUtil {
	private static Map<String, XStream> xsMap = new HashMap<String, XStream>();

	@SuppressWarnings("rawtypes")
	private static XStream getXStream(Class clazz) {
		String className = clazz.getName();
		if(xsMap.containsKey(className)) return xsMap.get(className);
		XStream xs = new XStream(new CdataXppDriver());
		xs.processAnnotations(clazz);
		xsMap.put(className, xs);
		return xs;
	}
	
	/**
	 * <pre>
	 * Method : XmlBindUtil.getXStreamWithNd()
	 * 	별도의 지원 드라이버 없이 XStream 생성
	 * <br>
	 * @param clazz
	 * @return 
	 * type of XStream
	 * </pre>
	 */	
	@SuppressWarnings("rawtypes")
	private static XStream getXStreamWithNd(Class clazz) {
		String className = clazz.getName();
		if(xsMap.containsKey(className)) return xsMap.get(className);
		XStream xs = new XStream();
		xs.processAnnotations(clazz);
		xsMap.put(className, xs);
		return xs;
	}

	@SuppressWarnings("rawtypes")
	private static XStream getXStreamWithNs(Class clazz, String[] ns) {
		String className = clazz.getName();
		if(xsMap.containsKey(className)) return xsMap.get(className);
		
		QNameMap qnameMap = new QNameMap();
		QName qname = new QName(ns[0], ns[1]);
		qnameMap.registerMapping(qname, clazz);
		
		XStream xs = new XStream(new StaxDriver(qnameMap));
		xs.processAnnotations(clazz);
		xsMap.put(className, xs);
		return xs;
	}

	/**
	 * 어노테이션을 이용한 xml binding
	 * @param <B>
	 * @param clazz
	 * @param object
	 * @return
	 */
	public static <B> String toXml(Class<B> clazz, Object object) {
		if(object==null) return null;
		XStream xs = getXStream(clazz);
		return xs.toXML(object);
	}

	public static <B> void toXml(Class<B> clazz, Object object, OutputStream os) {
		if(object==null) return;
		XStream xs = getXStream(clazz);
		xs.toXML(object, os);
	}

	public static <B> void toXml(Class<B> clazz, Object object, File file) throws FileNotFoundException {
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(file);
			toXml(clazz, object, fos);
		} finally {
			IOUtils.closeQuietly(fos);
		}
	}
	
	/**
	 * <pre>
	 * Method : XmlBindUtil.toXmlWithNd()
	 * 	getXStreamWithNd을 이용하여 xml 파일을 저장한다.
	 * <br>
	 * @param clazz
	 * @param object
	 * @param os
	 * @return 
	 * type of <B>
	 * </pre>
	 */	
	public static <B> void toXmlWithNd(Class<B> clazz, Object object, OutputStream os) {
		if(object==null) return;
		XStream xs = getXStreamWithNd(clazz);
		xs.toXML(object, os);
	}
	
	public static <B> void toXmlWithNd(Class<B> clazz, Object object, File file) throws FileNotFoundException {
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(file);
			toXmlWithNd(clazz, object, fos);
		} catch (Exception e) {
			IOUtils.closeQuietly(fos);
		}
	}

	/**
	 * 어노테이션을 이용한 xml binding
	 * @param <B>
	 * @param clazz
	 * @param xmlStr
	 * @return
	 */
	public static <B> B fromXml(Class<B> clazz, String xmlStr) {
		XStream xs = getXStream(clazz);
		return (B) xs.fromXML(xmlStr);
	}

	public static <B> B fromXml(Class<B> clazz, InputStream is) {
		XStream xs = getXStream(clazz);
		return (B) xs.fromXML(is);
	}

	public static <B> B fromXml(Class<B> clazz, File file) throws FileNotFoundException {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(file);
			return fromXml(clazz, fis);
		} finally {
			IOUtils.closeQuietly(fis);
		}
	}

	/**
	 * <pre>
	 * Method : XmlBindUtil.fromXmlWithNd()
	 * 	getXStreamWithNd을 이용하여 xml 파일을 불러온다.
	 * <br>
	 * @param clazz
	 * @param object
	 * @param os
	 * @return 
	 * type of <B>
	 * </pre>
	 */	
	public static <B> B fromXmlWithNd(Class<B> clazz, InputStream is) {
		XStream xs = getXStreamWithNd(clazz);
		return (B) xs.fromXML(is);
	}

	public static <B> B fromXmlWithNd(Class<B> clazz, File file) throws FileNotFoundException {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(file);
			return fromXmlWithNd(clazz, fis);
		} finally {
			IOUtils.closeQuietly(fis);
		}
	}
	
	/**
	 * 
	 * @param <B>
	 * @param clazz
	 * @param xmlStr
	 * @param ns	String[] ns = new String[]{"http://cc.icubecloud.com/v1/","ns1"};
	 * @return
	 */
	public static <B> B fromXmlWithNs(Class<B> clazz, String xmlStr, String[] ns) {
		XStream xs = getXStreamWithNs(clazz, ns);
		return (B) xs.fromXML(xmlStr);
	}

	public static <B> B fromXmlWithNs(Class<B> clazz, InputStream is, String[] ns) {
		XStream xs = getXStreamWithNs(clazz, ns);
		return (B) xs.fromXML(is);
	}

	public static <B> B fromXmlWithNs(Class<B> clazz, File file, String[] ns) throws FileNotFoundException {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(file);
			return fromXmlWithNs(clazz, fis, ns);
		} finally {
			IOUtils.closeQuietly(fis);
		}
	}
	
	/**
	 * 
	 * @param <B>
	 * @param clazz
	 * @param object
	 * @param ns	String[] ns = new String[]{"http://cc.icubecloud.com/v1/","ns1"};
	 * @return
	 */
	public static <B> String toXmlWithNs(Class<B> clazz, Object object, String[] ns) {
		if(object==null) return null;
		XStream xs = getXStreamWithNs(clazz, ns);
		return xs.toXML(object);
	}

	public static <B> void toXmlWithNs(Class<B> clazz, Object object, OutputStream os, String[] ns) {
		if(object==null) return;
		XStream xs = getXStreamWithNs(clazz, ns);
		xs.toXML(object, os);
	}

	public static <B> void toXml(Class<B> clazz, Object object, File file, String[] ns)
	throws FileNotFoundException {
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(file);
			toXmlWithNs(clazz, object, fos, ns);
		} finally {
			IOUtils.closeQuietly(fos);
		}
	}


}
